package com.zyz.judge.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zyz.api.model.UserExeResult;
import com.zyz.api.model.dto.JudgeSubmitDTO;
import com.zyz.api.model.vo.UserQuestionResultVO;
import com.zyz.common.core.constants.CacheConstants;
import com.zyz.common.core.constants.JudgeConstants;
import com.zyz.common.core.enums.CodeRunStatus;
import com.zyz.common.core.enums.ResultCodeEnum;
import com.zyz.common.redis.service.RedisService;
import com.zyz.common.security.exception.ServiceException;
import com.zyz.judge.mapper.UserSubmitMapper;
import com.zyz.judge.model.SandBoxExecuteResult;
import com.zyz.judge.model.UserSubmit;
import com.zyz.judge.service.IJudgeService;
import com.zyz.judge.service.ISandboxPoolService;
import com.zyz.judge.service.ISandboxService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class JudgeServiceImpl implements IJudgeService {

    @Autowired
    private ISandboxService sandboxService;

    @Autowired
    private UserSubmitMapper userSubmitMapper;

    @Autowired
    private ISandboxPoolService sandboxPoolService;

    @Autowired
    private RedisService redisService;


    @Override
    public UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {
        // 执行用户代码
        SandBoxExecuteResult sandBoxExecuteResult =
                sandboxPoolService.exeJavaCode(judgeSubmitDTO.getUserId(),judgeSubmitDTO.getUserCode(), judgeSubmitDTO.getInputList());
        UserQuestionResultVO userQuestionResultVO = new UserQuestionResultVO();
        log.info("sandBoxExecuteResult:{}",sandBoxExecuteResult);
        if (sandBoxExecuteResult != null && CodeRunStatus.SUCCEED.equals(sandBoxExecuteResult.getRunStatus())) {
            log.info("----开始对比结果----");
            doJudge (judgeSubmitDTO, sandBoxExecuteResult, userQuestionResultVO);
        } else {
            log.info("----程序执行出错----");
            failed (userQuestionResultVO, sandBoxExecuteResult);
        }
        saveUserSubmit(judgeSubmitDTO, userQuestionResultVO);
        if(userQuestionResultVO.getPass() == 1) {
            // 题目热榜排名加一
            updateHotList(judgeSubmitDTO.getQuestionId());
        }
        log.info("userQuestionResultVO(doJudgeJavaCode):{}", userQuestionResultVO);
        return userQuestionResultVO;
    }


    private static void failed(UserQuestionResultVO userQuestionResultVO, SandBoxExecuteResult sandBoxExecuteResult) {
        // 运行失败
        userQuestionResultVO.setPass(JudgeConstants.FALSE);
        if (sandBoxExecuteResult != null) {
            userQuestionResultVO.setExeMessage(sandBoxExecuteResult.getExeMessage());
        } else {
            userQuestionResultVO.setExeMessage(CodeRunStatus.UNKNOWN_FAILED.getMsg());
        }
        userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
    }

    private static void doJudge(JudgeSubmitDTO judgeSubmitDTO, SandBoxExecuteResult sandBoxExecuteResult, UserQuestionResultVO userQuestionResultVO) {
        // 运行成功
        List<String> exeOutputList = sandBoxExecuteResult.getOutputList();
        List<String> outputList = judgeSubmitDTO.getOutputList();

        List<UserExeResult> userExeResultList = new ArrayList<>();
        for (int index = 0; index < outputList.size(); index++) {
            String outputStr = outputList.get(index);
            String exeOutputStr = exeOutputList.get(index);
            String input = judgeSubmitDTO.getInputList().get(index);
            UserExeResult userExeResult = new UserExeResult();
            userExeResult.setInput(input);
            userExeResult.setOutput(outputStr);
            userExeResult.setExeOutput(exeOutputStr);
            userExeResultList.add(userExeResult);
        }
        userQuestionResultVO.setUserExeResultList(userExeResultList);
        // 结果不一致
        if (!outputList.equals(exeOutputList)) {
            log.info("----结果没通过----");
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setPass(JudgeConstants.FALSE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return;
        }
        // 结果一致  开始判断空间和时间限制
        if (sandBoxExecuteResult.getUseMemory() > judgeSubmitDTO.getSpaceLimit()) {
            log.info("----空间没通过----");
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setPass(JudgeConstants.FALSE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_MEMORY.getMsg());
            return;
        }
        if (sandBoxExecuteResult.getUseTime() > judgeSubmitDTO.getTimeLimit()) {
            log.info("----时间没通过----");
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setPass(JudgeConstants.FALSE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_TIME.getMsg());
            return;
        }
        // 执行通过
        log.info("----执行通过----");
        int score = judgeSubmitDTO.getDifficulty() * JudgeConstants.DEFAULT_SCORE;
        userQuestionResultVO.setScore(score);
        userQuestionResultVO.setPass(JudgeConstants.SUCCESS);
    }

    private void saveUserSubmit(JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO) {
        // 最后一次的结果 存入数据库中 (先拿到之前的数据库中的数据 删除 再插入)
        UserSubmit userSubmit = new UserSubmit();
        BeanUtil.copyProperties(userQuestionResultVO, userSubmit);
        userSubmit.setUserId(judgeSubmitDTO.getUserId());
        userSubmit.setQuestionId(judgeSubmitDTO.getQuestionId());
        userSubmit.setExamId(judgeSubmitDTO.getExamId());
        userSubmit.setProgramType(judgeSubmitDTO.getProgramType());
        userSubmit.setUserCode(judgeSubmitDTO.getUserCode());
        userSubmit.setCaseJudgeRes(JSON.toJSONString(userQuestionResultVO.getUserExeResultList()));
        userSubmit.setCreateBy(judgeSubmitDTO.getUserId());
        userSubmitMapper.delete(new LambdaQueryWrapper<UserSubmit>()
                .eq(UserSubmit::getUserId, judgeSubmitDTO.getUserId())
                .eq(UserSubmit::getQuestionId, judgeSubmitDTO.getQuestionId())
//                .isNull(judgeSubmitDTO.getExamId() == null, UserSubmit::getExamId)
                .eq(judgeSubmitDTO.getExamId() != null, UserSubmit::getExamId, judgeSubmitDTO.getExamId()));
        int row = userSubmitMapper.insert(userSubmit);
        if (row != 1) {
            log.error("用户答题信息同步数据库失败 userId: {} questionId: {}",
                    judgeSubmitDTO.getUserId(), judgeSubmitDTO.getQuestionId());
            throw new ServiceException(ResultCodeEnum.ERROR);
        }
    }

    /**
     * 更新对应题目热榜分数
     * @param questionId  题目id
     */
    private void updateHotList(Long questionId) {
        redisService.addOneZSet(CacheConstants.QUESTION_HOT, questionId);
    }
}
